from types import FunctionType

from flask import current_app, request
from flask_restx import abort, reqparse
from sqlalchemy import and_, or_

from app.models import userModel
from app.models.roleModel import PresetRoleChoices, Role, UserRole
from tools import db
from utils.utils import base_detail

from .base_view import OrganizerBase


class Member(OrganizerBase):
    def getUserInfo(self, username=None, id=None, email=None):
        user_info = None
        if username:
            user_info = userModel.User.query.filter_by(username=username).first()
        elif id:
            user_info = userModel.User.query.filter(userModel.User.id == id).first()
        elif email:
            user_info = userModel.User.query.filter(
                userModel.User.email == email
            ).first()
        return user_info

    def admin_identity_check(
        self, validate: FunctionType = None, kwargs: dict = None, err_msg: str = ""
    ):
        """部分操作身份检查
        :param func validate: 非管理员用户允许的操作条件
        :param dict kwargs: validate 参数
        :param str err_msg: 校验失败信息
        """
        current_role = request.cookies.get("role", None)

        # 管理员允许操作
        if self.user.is_superuser and current_role == PresetRoleChoices.admin:
            return True
        # 非管理员时对异常进行抛出
        if not validate(**kwargs):
            abort(403, message=err_msg)


class MemberList(Member):
    def get(self):
        params = request.args
        page = params.get("page", default=1, type=int)
        page_size = params.get("pageSize", default=10, type=int)
        keyword = params.get("keyword")
        role = params.get("role", default=None)

        if page_size >= 50:
            abort(400, message="每页数量不能超过50")

        if role and role not in PresetRoleChoices:
            abort(400, message="筛选角色不存在")
        elif role and role == PresetRoleChoices.admin:
            abort(400, message="参数选项被禁止")

        try:
            user_filter = [
                userModel.User.project_id == self.user.project_id,
            ]
            if keyword:
                # 添加关键词的检索选项
                user_filter.append(
                    or_(
                        userModel.User.username.contains(keyword),
                        userModel.User.account_name.contains(keyword),
                        userModel.User.email.contains(keyword),
                    )
                )

            role_filter = [UserRole.role_id == Role.id]
            if role:
                role_filter.append(Role.name == role)
            elif not (
                request.cookies.get("role", None) == PresetRoleChoices.admin
                and self.user.is_superuser
            ):
                role_filter.append(Role.name == PresetRoleChoices.student)

            paginate = (
                userModel.User.query.join(
                    UserRole, UserRole.user_id == userModel.User.id
                )
                .join(Role, and_(*role_filter))
                .filter(*user_filter)
                .order_by(userModel.User.id.desc())
                .group_by(userModel.User.id)
                .paginate(page, page_size, error_out=False)
            )

            info = dict()  # noqa
            info["current_page"] = page
            info["data"] = []
            if len(paginate.items) > 0:
                for content in paginate.items:
                    info["data"].append(
                        {
                            "username": content.username,
                            "realname": content.account_name,
                            "email": content.email,
                            "id": content.id,
                            "roles": [item.id for item in content.roles],
                        }
                    )
            info = base_detail(info, paginate)
            return {"code": 200, "data": info}

        except Exception as e:
            current_app.logger.exception(e)
            abort(500, message="参数错误")


class MemberPassword(Member):
    def validate_user(self, user_info: userModel.User = None):
        user_roles = [item.name for item in user_info.roles]
        # 修改自己的或者仅一个学生角色的用户
        if user_info.id == self.user.id or PresetRoleChoices.student == user_roles[0]:
            return True
        return False

    def put(self, id=None):
        user_info: userModel.User = self.getUserInfo(id=id)
        if not user_info:
            abort(400, message="参数错误")

        self.admin_identity_check(
            validate=self.validate_user,
            kwargs={"user_info": user_info},
            err_msg="无权修改该用户密码，请联系管理员操作",
        )

        parser = reqparse.RequestParser()
        parser.add_argument("password", type=str, help="请输入密码", required=True)
        parser.add_argument("confirm_password", type=str, help="输入确认密码", required=True)
        args = parser.parse_args()
        if not all([args["password"], args["confirm_password"]]):
            abort(400, message="参数错误")

        if args["password"] != args["confirm_password"]:
            abort(400, message="确认密码和密码不一致")

        with db.auto_commit():
            user_info.password = args["password"]
            userModel.db.session.commit()
            current_app.logger.info(
                self.user.username + "---action--MemberPassword--user:" + str(id)
            )
        return {"code": 200, "data": "密码修改成功"}


class MemberManage(Member):
    def get(self, id=None):
        user_info = self.getUserInfo(id=id)
        if not user_info:
            abort(400, message="该用户不存在")
        try:
            info = {
                "id": user_info.id,
                "username": user_info.username,
                "realname": user_info.account_name,
                "roles": [
                    {"id": item.id, "name": item.name}
                    for item in sorted(user_info.roles, key=lambda x: x.id)
                ],
                "email": user_info.email,
            }
            return {"code": 200, "data": info}

        except Exception as e:
            current_app.logger.exception(e)
            abort(500, message="参数错误")

    @staticmethod
    def validate_role_ids(role_ids=None):
        return role_ids is None

    @staticmethod
    def validate_user_role(user_info: userModel.User):
        user_roles = [item.name for item in user_info.roles]
        # 仅一个学生角色的用户
        if PresetRoleChoices.student == user_roles[0]:
            return True
        return False

    def post(self):
        parser = reqparse.RequestParser()
        parser.add_argument("username", type=str, help="账户不能为空", required=True)
        parser.add_argument("realname", type=str, help="姓名不能为空", required=True)
        parser.add_argument("password", type=str, help="请输入密码", required=True)
        parser.add_argument("confirm_password", type=str, help="输入确认密码", required=True)
        parser.add_argument("email", type=str, help="邮箱不能为空", required=True)
        parser.add_argument("role_ids", type=list, location="json", default=None)
        args = parser.parse_args()
        if not all(
            [
                args["username"],
                args["realname"],
                args["password"],
                args["confirm_password"],
                args["email"],
            ]
        ):
            abort(400, message="参数错误")

        self.admin_identity_check(
            validate=self.validate_role_ids,
            kwargs={"role_ids": args["role_ids"]},
            err_msg="当前新建用户时不允许赋予其他角色",
        )

        if args["password"] != args["confirm_password"]:
            abort(400, message="确认密码和密码不一致")

        # 判断账户是否存在
        user_info = self.getUserInfo(args["username"])
        if user_info:
            abort(400, message="账户名已存在，请更换！")

        # 判断邮箱
        user_info_by_email = self.getUserInfo(email=args["email"])
        if user_info_by_email:
            abort(400, message="电子邮箱已存在，请更换！")

        with db.auto_commit():
            obj = userModel.User(
                username=args["username"],
                password=args["password"],
                email=args["email"],
                account_name=args["realname"],
                role_id=0,
                project_id=self.user.project_id,
            )
            db.session.add(obj)
            db.session.flush()
            if args["role_ids"]:
                obj.roles = (
                    Role.query.filter(Role.id.in_(args["role_ids"])).all() or obj.roles
                )
            else:
                obj.roles = Role.query.filter(
                    Role.name == PresetRoleChoices.student.value
                ).all()
            current_app.logger.info(
                self.user.username + "---action--add--user:" + args["username"]
            )
        return {"code": 200, "data": "创建成功"}

    def put(self, id=None):
        if not id:
            abort(400, message="参数错误")

        parser = reqparse.RequestParser()
        parser.add_argument("username", type=str, help="账户不能为空", required=True)
        parser.add_argument("realname", type=str, help="姓名不能为空", required=True)
        parser.add_argument("email", type=str, help="请输入地址邮箱", required=True)
        parser.add_argument("role_ids", type=list, location="json", default=None)
        args = parser.parse_args()
        if not all([args["username"], args["realname"], args["email"]]):
            abort(400, message="参数错误")

        # 根据id获取用户信息
        user_info = self.getUserInfo(id=id)
        if not user_info:
            abort(400, message="该用户不存在")

        is_admin = (
            self.user.is_superuser
            and request.cookies.get("role", None) == PresetRoleChoices.admin
        )
        if user_info.is_superuser == 1:
            if not is_admin:
                abort(400, message="非管理员不允许修改拥有管理员权限账户")
            elif not args["role_ids"]:
                abort(400, message="不允许置空超级用户角色")
            elif args["role_ids"] and not db.exists(
                Role.query.filter(
                    Role.id.in_(args["role_ids"]), Role.name == PresetRoleChoices.admin
                )
            ):
                abort(400, message="不允许清除管理员角色")

        self.admin_identity_check(
            validate=self.validate_role_ids,
            kwargs={"role_ids": args["role_ids"]},
            err_msg="当前修改用户时不允许赋予其他角色",
        )

        # 判断邮箱
        user_info_by_email = self.getUserInfo(email=args["email"])
        if user_info_by_email and user_info_by_email.id != id:
            abort(400, message="电子邮箱已存在，请更换！")

        with db.auto_commit():
            user_info.email = args["email"]
            user_info.account_name = args["realname"]

            if args["role_ids"]:
                _default = user_info.roles
                user_info.roles = (
                    Role.query.filter(Role.id.in_(args["role_ids"])).all() or _default
                )

            db.session.add(user_info)

        current_app.logger.info(
            self.user.username + "---action--put--user id:" + str(id)
        )
        return {"code": 200, "data": "修改成功"}

    def delete(self, id=None):
        user_info: userModel.User = self.getUserInfo(id=id)
        if not user_info:
            abort(400, message="user not exist")

        # 用户多角色时可能出现的情况
        if user_info.id == self.user.id:
            abort(400, message="不允许删除自身")

        self.admin_identity_check(
            validate=self.validate_user_role,
            kwargs={"user_info": user_info},
            err_msg="该用户存在其他角色，请联系管理员删除",
        )

        current_role = request.cookies.get("role", None)
        if current_role != PresetRoleChoices.admin and len(user_info.roles) > 1:
            abort(400, message="不允许删除多角色用户，请联系管理员删除")

        if user_info.CtfLinkUser:
            for ctf in user_info.CtfLinkUser:
                if ctf.Ctf is not None:
                    abort(400, message="此账号已关联比赛，无法删除")

        if user_info.groups.count():
            abort(400, message="此账号已关联红蓝比赛，无法删除")

        if user_info.courses.count() >= 1:
            abort(400, message="此账号已关联课程，无法删除")

        userModel.db.session.delete(user_info)
        userModel.db.session.commit()
        return {"code": 200, "data": "删除成功"}


class UsersBulkOperationsAPI(Member):

    """兼容用户批量上传"""

    def get(self):
        from app.api.v1.admin.users import UsersBulkOperationsAPI

        heads = UsersBulkOperationsAPI.student_heads
        response = UsersBulkOperationsAPI.get_import_template(heads)
        return response

    def post(self):
        from app.api.v1.admin.users import UsersBulkOperationsAPI

        heads = UsersBulkOperationsAPI.student_heads
        file = request.files.get("file")
        message = UsersBulkOperationsAPI.excel_handle(file, heads)
        return {"code": 200, "message": message}
